﻿using System;
using System.IO;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tao.DevIl;
using PocketEngine.Engine;
using Tao.OpenGl;

namespace PocketEngine.Images
{
    public enum pImageFormat
    {
        Rgb,Rgba,Depth
    }
    public class pImage
    {
        public string Path = string.Empty;
        ~pImage()
        {
            pEngine.Garbage.AddTexture(mGL);

            //Gl.glEnable(Gl.GL_TEXTURE_2D);
            //Gl.glDeleteTextures(1, ref mGL);
        }
        public pImage Normals
        {
            get
            {
                return mNormals;
            }
            set
            {
                mNormals = value;
            }
        }
        private pImage mNormals = null;

        public pImage Speculars
        {
            get
            {
                return mSpeculars;
            }
            set
            {
                mSpeculars = value;
            }
        }
        private pImage mSpeculars = null;

        public int Width
        {
            get
            {
                return mWidth;
            }
            set
            {
                mWidth = value;
            }
        }
        private int mWidth = 0;
        public int Height
        {
            get
            {
                return mHeight;
            }
            set
            {
                mHeight = value;
            }
        }
        private int mHeight = 0;
        public int Depth
        {
            get
            {
                return mDepth;
            }
            set
            {
                mDepth = value;
            }
        }
        private int mDepth = 0;
        public int BytesPerPixel
        {
            get
            {
                return mBpp;
            }
            set
            {
                mBpp = value;
            }
        }
        private int mBpp = 0;
        public pImageFormat Format
        {
            get
            {
                return mFormat;
            }
            set
            {
                mFormat = value;
            }
        }
        private pImageFormat mFormat;

        public int GLTexture
        {
            get
            {
                return mGL;
            }
            set
            {
                mGL = value;
            }
        }
        private int mGL = 0;

        public pImage(string path)
        {
            Path = path;
            if(!path.Contains("NRM") && !path.Contains("SPEC"))
            {
                FileInfo info = new FileInfo(path);
                
                string file = info.Name;
                
                int mp = file.LastIndexOf(".");
                file = file.Substring(0, mp);

                string basePath = info.DirectoryName;

                string ext = info.Extension;

                string normFile = basePath + "/" + file + "_NRM"+ext;
                string specFile = basePath + "/" + file + "_SPEC"+ext;

                if (new FileInfo(normFile).Exists)
                {
                    Normals = new pImage(normFile);
                    Console.WriteLine("Parsed Normals.");
                }
                
                if (new FileInfo(specFile).Exists)
                {
                    Speculars = new pImage(specFile);
                    Console.WriteLine("Parsed Speculars.");
                }

            }

            if (!new FileInfo(path).Exists)
            {
                pEngine.Err("Image at path:" + path + " does not exist.", "pImage.pImage(path)");
            }

            int id = 0;
            Il.ilGenImages(1, out id);
            Il.ilBindImage(id);
            if (!Il.ilLoadImage(path))
            {
                pEngine.Err("Could not load image.\nPath:" + path, "pImage.pImage(path)");
            }
            Width = Il.ilGetInteger(Il.IL_IMAGE_WIDTH);
            Height = Il.ilGetInteger(Il.IL_IMAGE_HEIGHT);
            Depth = Il.ilGetInteger(Il.IL_IMAGE_DEPTH);
            BytesPerPixel = Il.ilGetInteger(Il.IL_IMAGE_BYTES_PER_PIXEL);
            switch (BytesPerPixel)
            {
                case 1:
                    Format = pImageFormat.Depth; 
                   break;
                case 2:
                   pEngine.Err("Unsupported internal format.path:" + path,"pImage.pImage(path)");
                   break;
                case 3:
                   Format = pImageFormat.Rgb;
                   break;
                case 4:
                   Format = pImageFormat.Rgba;
                   break;
                default:
                   pEngine.Err("Unsupported internal format>4.path:" + path, "pImage.pImage(path)");
                   break;
            }
           
            Console.WriteLine("Parsed Image.");
            Console.WriteLine("Path:" + path);
            Console.WriteLine("W:" + Width);
            Console.WriteLine("H:" + Height);
            Console.WriteLine("Bpp:" + BytesPerPixel);
            Console.WriteLine("Format:" + Format.ToString());
            
            Gl.glEnable(Gl.GL_TEXTURE_2D);
            Gl.glGenTextures(1,out mGL);
            Gl.glBindTexture(Gl.GL_TEXTURE_2D, mGL);
            Gl.glPixelStorei(Gl.GL_UNPACK_ROW_LENGTH, Width); 
            Gl.glPixelStorei(Gl.GL_UNPACK_ALIGNMENT, 1) ;

            switch (Format)
            {
                case pImageFormat.Rgb:
                    Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGB, Width, Height, 0, Gl.GL_RGB, Gl.GL_UNSIGNED_BYTE, Il.ilGetData());
                    break;
                case pImageFormat.Rgba:
                    Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGBA, Width, Height, 0, Gl.GL_RGBA, Gl.GL_UNSIGNED_BYTE, Il.ilGetData());
                    break;
            }

           
           // Il.ilDeleteImages(1, ref id);
            Data = Il.ilGetData();
            Il.ilBindImage(0);
            Gl.glGenerateMipmapEXT(Gl.GL_TEXTURE_2D);
            Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_LINEAR_MIPMAP_LINEAR);
            Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_LINEAR);

            Gl.glBindTexture(Gl.GL_TEXTURE_2D, 0);
            Gl.glDisable(Gl.GL_TEXTURE_2D);

        }
        public MemoryStream ToStream()
        {
            byte[] nbuf = new byte[Width * Height * BytesPerPixel];

            unsafe
            {
                void* buf = Data.ToPointer();
                byte* bufb = (byte*)buf;
                for (int x = 0; x < Width-1; x++)
                {
                    for (int y = 0; y < Height-1; y++)
                    {
                        int off = (y * Width * BytesPerPixel) + x * BytesPerPixel;
                        nbuf[off] = bufb[off];
                        nbuf[off + 1] = bufb[off + 1];
                        nbuf[off + 2] = bufb[off + 2];
                        if (BytesPerPixel == 4)
                        {
                            nbuf[off + 3] = bufb[off + 3];
                        }
                    }
                }
            }
            return new MemoryStream(nbuf, 0, nbuf.Length);       
        }
        public IntPtr Data = IntPtr.Zero;
        public pImage(int width, int height, pImageFormat format)
        {
            Width = width;
            Height = height;
            Format = format;

            Gl.glEnable(Gl.GL_TEXTURE_2D);
            Gl.glGenTextures(1, out mGL);
            Gl.glBindTexture(Gl.GL_TEXTURE_2D, mGL);
            Gl.glPixelStorei(Gl.GL_UNPACK_ROW_LENGTH, Width);
            Gl.glPixelStorei(Gl.GL_UNPACK_ALIGNMENT, 1);

            switch (Format)
            {
                case pImageFormat.Rgb:
                    mBpp = 3;
                    Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGB, Width, Height, 0, Gl.GL_RGB, Gl.GL_UNSIGNED_BYTE, IntPtr.Zero);
                    break;
                case pImageFormat.Rgba:
                    mBpp = 4;
                    Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGBA, Width, Height, 0, Gl.GL_RGBA, Gl.GL_UNSIGNED_BYTE, IntPtr.Zero);
                    break;
            }

        
        
            Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_LINEAR);
            Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_LINEAR);

            Gl.glBindTexture(Gl.GL_TEXTURE_2D, 0);
            Gl.glDisable(Gl.GL_TEXTURE_2D);

        }
        GCHandle Handle;
        public pImage(byte[] buf,int width, int height, pImageFormat format)
        {
            Width = width;
            Height = height;
            Format = format;
            Handle = GCHandle.Alloc(buf, GCHandleType.Pinned);
            Data = Handle.AddrOfPinnedObject();
                Gl.glEnable(Gl.GL_TEXTURE_2D);
            Gl.glGenTextures(1, out mGL);
            Gl.glBindTexture(Gl.GL_TEXTURE_2D, mGL);
            Gl.glPixelStorei(Gl.GL_UNPACK_ROW_LENGTH, Width);
            Gl.glPixelStorei(Gl.GL_UNPACK_ALIGNMENT, 1);

            switch (Format)
            {
                case pImageFormat.Rgb:
                    mBpp = 3;
                    Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGB, Width, Height, 0, Gl.GL_RGB, Gl.GL_UNSIGNED_BYTE, Data);
                    break;
                case pImageFormat.Rgba:
                    mBpp = 4;
                    Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGBA, Width, Height, 0, Gl.GL_RGBA, Gl.GL_UNSIGNED_BYTE, Data);
                    break;
            }



            Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_LINEAR);
            Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_LINEAR);

            Gl.glBindTexture(Gl.GL_TEXTURE_2D, 0);
            Gl.glDisable(Gl.GL_TEXTURE_2D);

        }
        public void Bind(int unit)
        {
            Gl.glActiveTexture(Gl.GL_TEXTURE0 + unit);
            Gl.glClientActiveTexture(Gl.GL_TEXTURE0 + unit);
            Gl.glEnable(Gl.GL_TEXTURE_2D);
            Gl.glBindTexture(Gl.GL_TEXTURE_2D, mGL);

        }
        public void Unbind(int unit)
        {
            Gl.glActiveTexture(Gl.GL_TEXTURE0 + unit);
            Gl.glClientActiveTexture(Gl.GL_TEXTURE0 + unit);
            Gl.glBindTexture(Gl.GL_TEXTURE_2D, 0);
            Gl.glDisable(Gl.GL_TEXTURE_2D);
        }

    }
}
